﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using Ninject;

using SSTU.Contract.Common;
using SSTU.Contract.Facade;
using SSTU.Contract.Interaction;
using SSTU.Management.Dependency;
using SSTU.Model;
using SSTU.ViewModel.Core;
using SSTU.ViewModel.Item;

namespace SSTU.ViewModel
{
	/// <summary>
	/// View model for faculty menu.
	/// </summary>
	public class FacultyMenuViewModel : AppViewModelBase
	{
		private readonly IStructureFacade _structureFacade;
		private readonly INavigationInteraction _navigationInteraction;

		private string _shortName;
		private string _longName;
		private DelegateCommand<GroupItemVM> _favoriteCommand;

		/// <summary>
		/// Initializes a new instance of the <see cref="FacultyMenuViewModel"/> class.
		/// </summary>
		/// <param name="structureFacade">The structure facade.</param>
		/// <param name="navigationInteraction">The navigation interaction.</param>
		[Inject]
		public FacultyMenuViewModel(
			IStructureFacade structureFacade,
			INavigationInteraction navigationInteraction)
		{
			DependencyRequirement.Current.NotNullFromConstructorInjection<FacultyMenuViewModel, IStructureFacade>(structureFacade);
			DependencyRequirement.Current.NotNullFromConstructorInjection<FacultyMenuViewModel, INavigationInteraction>(navigationInteraction);
			_structureFacade = structureFacade;
			_navigationInteraction = navigationInteraction;
			Groups = new ObservableCollection<GroupItemVM>();
			FavoriteCommand = new DelegateCommand<GroupItemVM>(ExecuteFavoriteCommand, CanExecuteFavoriteCommand);
		}

		/// <summary>
		/// Gets or sets the short name (abbreviation).
		/// </summary>
		public string ShortName
		{
			get { return _shortName; }
			set
			{
				if (value != _shortName)
				{
					RaisePropertyChanging<string>(() => ShortName);
					_shortName = value;
					RaisePropertyChanged<string>(() => ShortName);
				}
			}
		}

		/// <summary>
		/// Gets or sets the long name.
		/// </summary>
		public string LongName
		{
			get { return _longName; }
			set
			{
				if (value != _longName)
				{
					RaisePropertyChanging<string>(() => LongName);
					_longName = value;
					RaisePropertyChanged<string>(() => LongName);
				}
			}
		}

		/// <summary>
		/// Gets the observable collection of the groups.
		/// </summary>
		public ObservableCollection<GroupItemVM> Groups
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the command for changing 'FavoriteState' of the group item.
		/// </summary>
		public DelegateCommand<GroupItemVM> FavoriteCommand
		{
			get { return _favoriteCommand; }
			set
			{
				if (value != _favoriteCommand)
				{
					RaisePropertyChanging<DelegateCommand<GroupItemVM>>(() => FavoriteCommand);
					_favoriteCommand = value;
					RaisePropertyChanged<DelegateCommand<GroupItemVM>>(() => FavoriteCommand);
				}
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether in this instance groups from server is loaded.
		/// </summary>
		public bool IsGroupsFromServerLoaded { get; set; }

		/// <summary>
		/// Initializes the specified department item.
		/// </summary>
		/// <param name="departmentItem">The faculty item.</param>
		public void Initialize(FacultyItemVM facultyItem)
		{
			ShortName = facultyItem.ShortName;
			LongName = facultyItem.LongName;
			Groups.Clear();
			IsGroupsFromServerLoaded = false;
			UpdateIsBusy();
			_structureFacade.LoadGroupsAsync(facultyItem.Id, OnGroupsLoaded);
		}

		/// <summary>
		/// Called when groups loaded.
		/// </summary>
		/// <param name="response">The response.</param>
		private void OnGroupsLoaded(FacadeResponse<IEnumerable<Group>> response)
		{
			if (!IsGroupsFromServerLoaded)
			{
				if (response.DataSource == FacadeResponseDataSource.Server)
				{
					IsGroupsFromServerLoaded = true;
				}
				bool isSuccess = (response.IsSuccess && (response.Data != null));
				if (isSuccess)
				{
					Groups.Clear();
					foreach (var group in response.Data.OrderBy(g => g.ShortName))
					{
						var favoriteState = group.IsFavorite ? FavoriteState.Favorite : FavoriteState.NotFavorite;
						var groupItem = new GroupItemVM(group.Id, group.ShortName, group.LongName, favoriteState);
						Groups.Add(groupItem);
					}
				}
				else
				{
					DisplayError(response.DataSource, "списка групп", response.Error);
				}
				UpdateIsBusy();
			}
		}

		/// <summary>
		/// Updates the 'busy' status.
		/// </summary>
		private void UpdateIsBusy()
		{
			IsBusy = !IsGroupsFromServerLoaded;
		}

		/// <summary>
		/// Opens the group, navigates to the page with group time table.
		/// </summary>
		/// <param name="groupItem">The group item.</param>
		public void OpenGroup(GroupItemVM groupItem)
		{
			_navigationInteraction.ToGroupTimeTable(groupItem);
		}

		/// <summary>
		/// Executes the favorite command, change the 'FavoriteState' of the group item.
		/// </summary>
		/// <param name="groupItem">The group item.</param>
		private void ExecuteFavoriteCommand(GroupItemVM groupItem)
		{
			if (groupItem.FavoriteState == FavoriteState.NotFavorite)
			{
				groupItem.FavoriteState = FavoriteState.ChangingToFavorite;
				FavoriteCommand.RaiseCanExecuteChanged();
				_structureFacade.AddGroupToFavoritesAsync(groupItem.Id, response =>
				{
					groupItem.FavoriteState = response.IsSuccess ? FavoriteState.Favorite : FavoriteState.NotFavorite;
					FavoriteCommand.RaiseCanExecuteChanged();
				});
			}
			else if (groupItem.FavoriteState == FavoriteState.Favorite)
			{
				groupItem.FavoriteState = FavoriteState.ChangingToNotFavorite;
				FavoriteCommand.RaiseCanExecuteChanged();
				_structureFacade.RemoveGroupFromFavoritesAsync(groupItem.Id, response =>
				{
					groupItem.FavoriteState = response.IsSuccess ? FavoriteState.NotFavorite : FavoriteState.Favorite;
					FavoriteCommand.RaiseCanExecuteChanged();
				});
			}
		}

		/// <summary>
		/// Determines whether this instance can execute favorite command, for the specified group item.
		/// </summary>
		/// <param name="groupItem">The group item.</param>
		private bool CanExecuteFavoriteCommand(GroupItemVM groupItem)
		{
			bool result =
				groupItem != null &&
				!string.IsNullOrEmpty(groupItem.Id) && (
					groupItem.FavoriteState == FavoriteState.Favorite ||
					groupItem.FavoriteState == FavoriteState.NotFavorite);
			return result;
		}
	}
}
